/*
 * DatePrototype.java - ScriptME
 * 
 * Copyright (c) 2009 Cesar Henriques <cesar at alttab.com.ar>.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Based on FESI Project
 * 
 * Contributors:
 * 	Jean-Marc Lugrin - initial API and implementation
 * 	Cesar Henriques <cesar at alttab.com.ar> - J2ME Porting and Extensions
 */
package org.scriptme.ecmascript.data;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import org.scriptme.ecmascript.exceptions.EcmaScriptException;
import org.scriptme.ecmascript.interpreter.Evaluator;

/**
 * Implements the prototype and is the class of all Date objects.
 */
public class DatePrototype extends ESObject {

	// The value
	/** The date. */
	protected Date date = null;

	/**
	 * Create a new Date object with a null date.
	 * 
	 * @param prototype
	 *            the Date prototype
	 * @param evaluator
	 *            the Evaluator
	 */
	DatePrototype(ESObject prototype, Evaluator evaluator) {
		super(prototype, evaluator);
		date = new Date();
	}

	/**
	 * Create a new Date object with a specified date.
	 * 
	 * @param evaluator
	 *            the Evaluator
	 * @param aDate
	 *            the a date
	 */
	public DatePrototype(Evaluator evaluator, Date aDate) {
		super(evaluator.getDatePrototype(), evaluator);
		date = new Date(aDate.getTime());
	}

	/**
	 * Create a new Date object with a specified date.
	 * 
	 * @param evaluator
	 *            the Evaluator
	 * @param time
	 *            the Date
	 */
	public DatePrototype(Evaluator evaluator, long time) {
		super(evaluator.getDatePrototype(), evaluator);
		date = new Date(time);
	}

	// overrides
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.ESObject#getESClassName()
	 */
	public String getESClassName() {
		return "Date";
	}

	/**
	 * Set the year value of the date. BEWARE: Fixed as base 1900 !
	 * 
	 * @param arguments
	 *            The array of arguments, the first one being the year
	 * 
	 * @return the new date as a number
	 * 
	 * @throws EcmaScriptException
	 *             the ecma script exception
	 */
	public ESValue setYear(ESValue[] arguments) throws EcmaScriptException {
		if (date == null) {
			return new ESNumber(Double.NaN);
		} else {
			if (arguments.length <= 0) {
				date = null;
				return new ESNumber(Double.NaN);
			}
			Calendar cal = Calendar.getInstance(TimeZone.getDefault());
			cal.setTime(date);
			double d = arguments[0].doubleValue();
			if (Double.isNaN(d)) {
				date = null;
				return new ESNumber(Double.NaN);
			}
			if (d < 100)
				d += 1900;
			// System.out.println("SETYEAR to " + d);

			cal.set(Calendar.YEAR, (int) d);
			date = cal.getTime();
			long t = date.getTime();
			return new ESNumber((double) t);
		}
	}

	/**
	 * Set the time value of the date based on the element type to change Assume
	 * that the time elements are in the local time zone.
	 * 
	 * @param arguments
	 *            The array of arguments
	 * @param argTypes
	 *            the arg types
	 * 
	 * @return the new date as a number
	 * 
	 * @throws EcmaScriptException
	 *             the ecma script exception
	 * 
	 * @para, argTypes The array of element type
	 */
	public ESValue setTime(ESValue[] arguments, int[] argTypes)
			throws EcmaScriptException {
		if (date == null) {
			return new ESNumber(Double.NaN);
		} else {
			if (arguments.length <= 0) {
				date = null;
				return new ESNumber(Double.NaN);
			}
			Calendar cal = Calendar.getInstance(TimeZone.getDefault());
			cal.setTime(date);
			for (int iarg = 0; (iarg < argTypes.length)
					&& (iarg < arguments.length); iarg++) {
				double d = arguments[iarg].doubleValue();
				if (Double.isNaN(d)) {
					date = null;
					return new ESNumber(Double.NaN);
				}
				// System.out.println("SET " + argTypes[iarg] + " to " + d);
				cal.set(argTypes[iarg], (int) d);
			}
			date = cal.getTime();
			long t = date.getTime();
			return new ESNumber((double) t);
		}
	}

	/**
	 * Set the time value of the date based on the element type to change Assume
	 * that the time elements are in the UTC time zone.
	 * 
	 * @param arguments
	 *            The array of arguments
	 * @param argTypes
	 *            the arg types
	 * 
	 * @return the new date as a number
	 * 
	 * @throws EcmaScriptException
	 *             the ecma script exception
	 * 
	 * @para, argTypes The array of element type
	 */
	public ESValue setUTCTime(ESValue[] arguments, int[] argTypes)
			throws EcmaScriptException {
		if (date == null) {
			return new ESNumber(Double.NaN);
		} else {
			if (arguments.length <= 0) {
				date = null;
				return new ESNumber(Double.NaN);
			}
			Calendar cal = Calendar.getInstance(TimeZone.getDefault());
			cal.setTime(date);
			for (int iarg = 0; (iarg < argTypes.length)
					&& (iarg < arguments.length); iarg++) {
				double d = arguments[iarg].doubleValue();
				if (Double.isNaN(d)) {
					date = null;
					return new ESNumber(Double.NaN);
				}
				// System.out.println("UTCSET " + argTypes[iarg] + " to " + d);
				cal.set(argTypes[iarg], (int) d);
			}
			date = cal.getTime();
			long t = date.getTime();
			return new ESNumber((double) t);
		}
	}

	/**
	 * Get an element of the date (in local time zone).
	 * 
	 * @param element
	 *            The type of the element
	 * 
	 * @return the element as a value
	 */
	public ESValue get(int element) {
		if (date == null) {
			return new ESNumber(Double.NaN);
		} else {
			Calendar cal = Calendar.getInstance(TimeZone.getDefault());
			cal.setTime(date);
			long t = cal.get(element);
			// EcmaScript has SUNDAY=0, java SUNDAY=1 - converted in
			// DatePrototype
			if (element == Calendar.DAY_OF_WEEK)
				t--;
			return new ESNumber((double) t);
		}
	}

	/**
	 * Get an element of the date (in UTC time zone).
	 * 
	 * @param element
	 *            The type of the element
	 * 
	 * @return the element as a value
	 */
	public ESValue getUTC(int element) {
		if (date == null) {
			return new ESNumber(Double.NaN);
		} else {
			Calendar cal = Calendar.getInstance(TimeZone.getDefault());
			cal.setTime(date);
			long t = cal.get(element);
			// EcmaScript has SUNDAY=0, java SUNDAY=1 - converted in
			// DatePrototype
			if (element == Calendar.DAY_OF_WEEK)
				t--;
			return new ESNumber((double) t);
		}
	}

	// overrides
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.ESObject#toString()
	 */
	public String toString() {
		return (date == null ? "null" : date.toString());
	}

	// overrides
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.ESObject#toDetailString()
	 */
	public String toDetailString() {
		return "ES:[Object: builtin " + this.getClass().getName() + ":"
				+ ((date == null) ? "null" : date.toString()) + "]";
	}

	// overrides
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.ESObject#toJavaObject()
	 */
	public Object toJavaObject() {
		return date;
	}

	// overrides
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.ESObject#getDefaultValue()
	 */
	public ESValue getDefaultValue() throws EcmaScriptException {
		return this.getDefaultValue(EStypeString);
	}
}
